import { EventEmitter } from "events";
import { BaseResp } from "../../../entity/BaseEntity";
import { IGlobal } from "../../../entity/Global";
import { IProtocol } from "../protocol/IProtocol";
import { IProtocolData } from "../protocol/IProtocolData";
import { WsServer } from "../server/WsServer";
const WebSocket = require('ws')
declare var global: IGlobal;

type SocketMessageBackHandle = (data: BaseResp) => void;
export interface IWsClientOptions {
    /**通讯协议 */
    protocol?: IProtocol;
    /**是否自动重连，默认是 */
    autoReconnect?: boolean;
    /**自动重连次数，默认是number最大值，也就是一直重连 */
    reconnectCount?: number;
    /**自动重连延时（秒），默认10秒 ，实际延时时间=ceil(重连尝试次数/5)*reconnectDelay*/
    reconnectDelay?: number;
}

export class WsClient extends EventEmitter {
    private _ws: any;
    private _host: string;
    private _protocol: IProtocol;
    private _idIndex: number;
    private _autoReconnect: boolean;
    private _reconnectCount: number;
    private _reconnectDelay: number;
    private _connectCount: number;
    private _callbackHandle: { [id: number]: SocketMessageBackHandle };
    private _reconnectTimer:any;
    constructor(opt?: IWsClientOptions) {
        super();
        this._protocol = opt && opt.protocol || WsServer.DefaultProtocol;
        this._autoReconnect = opt && opt.autoReconnect || true;
        this._reconnectCount = opt && opt.reconnectCount || Number.MAX_SAFE_INTEGER;
        this._reconnectDelay = opt && opt.reconnectDelay || 10;
        this._connectCount = 0;
    }

    public connect(host: string, protocol?: string) {
        this.close();
        this._host = host;
        this._idIndex = 0;
        this._ws = new WebSocket(host, protocol);
        var self = this;
        this._ws.on('message', (e) => {
            self._onMessage(e);
        });
        this._ws.on('close', (e) => {
            self._onClose(e);
        });

        this._ws.on('error', (e) => {
            self._onError(e);
        });
        this._ws.on("open", (e) => {
            self._onOpen(e);
        });
    }

    public get readyState() {
        return this._ws ? this._ws.readyState : WebSocket.CLOSED;
    }

    public send(cmd: string, data: any, callback?: SocketMessageBackHandle) {
        let id = ++this._idIndex;
        var buffer = this._protocol.encode(cmd, data, id);
        if (callback != null) {
            this._callbackHandle[id] = callback;
        }
        this._ws.send(buffer);
    }

    public async sendAsync(cmd: string, data: any): Promise<BaseResp> {
        return new Promise((resolve, reject) => {
            this.send(cmd, data, (response: BaseResp) => {
                resolve(response);
            })
        });
    }

    public close() {
        this._clear();
    }

    private _onOpen(e) {
        this.emit("connect", e);
        this._connectCount=0;
        this._startHeart();
    }

    private _onMessage(msg: any) {
        var msgData: IProtocolData;
        try {
            msgData = this._protocol.decode(msg);
        } catch (e) {
            global.log.error(this._host + ":" + msg);
            return;
        }
        //心跳直接过滤掉
        if (msgData == null) return;
        let id = msgData.id;
        let callback: SocketMessageBackHandle = this._callbackHandle[id];
        if (callback != null) callback(msgData.data);
        this.emit("message", msgData);
    }

    private _onClose(e: any) {
        this._stopHeart();
        if (this._ws != null && this._autoReconnect && this._connectCount < this._reconnectCount) {
            this._reconnect();
        } else {
            this.emit("close", e);
        }
    }

    private _onError(e: any) {
        this.emit("error", e);
    }

    private _heartTime: any;
    private _startHeart() {
        this._stopHeart();
        this._heartTime = setInterval(this._sendHeart.bind(this), 10000);
    }

    private _stopHeart() {
        if (this._heartTime) {
            clearInterval(this._heartTime);
            this._heartTime = null;
        }
    }

    private _sendHeart() {
        this._ws.send("heart");
    }

    private _reconnect() {
        this.emit("reconnect");
        this._connectCount++;
        let delayTime=this._reconnectDelay*(Math.ceil(this._connectCount/5))*1000;
        this._reconnectTimer=setTimeout(() => {
            this._reconnectTimer=null;
            this.connect(this._host);        
        }, delayTime);
    }

    private _clear(){
        if (this._ws != null) {
            if (this._ws.CONNECTING || this._ws.OPEN) {
                this._ws.close();
            }
            this._ws = null;
        }
        if(this._reconnectTimer!=null){
            clearTimeout(this._reconnectTimer);
            this._reconnectTimer=null;
        }
        this._callbackHandle = {};
    }
}